﻿using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using Creventive.Wpf.ShellFactory.Workspace.Primitives;

namespace Creventive.Wpf.ShellFactory.Workspace
{
    /// <summary>
    /// Represents a tab strip which presents a tab for every view in a given workspace.
    /// </summary>
    public class WorkspaceTabStrip : TabControl
    {
        #region Static Members

        /// <summary>
        /// Provides a binding backing field for the Workspace property.
        /// </summary>
        public static readonly DependencyProperty WorkspaceProperty = DependencyProperty.Register("Workspace", typeof(IMultiViewWorkspace), typeof(WorkspaceTabStrip), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, (s, e) => ((WorkspaceTabStrip)s).OnWorkspaceChanged((IMultiViewWorkspace)e.OldValue, (IMultiViewWorkspace)e.NewValue)));

        static WorkspaceTabStrip()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(WorkspaceTabStrip), new FrameworkPropertyMetadata(typeof(WorkspaceTabStrip)));
        }

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="WorkspaceTabStrip"/> class.
        /// </summary>
        public WorkspaceTabStrip()
        {
            this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Close, OnCloseExecuted, this.OnCanExecuteClose));
        }

        /// <summary>
        /// Called when an input element requests the closing of a given view.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void OnCloseExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            var view = e.Parameter as ApplicationView;
            if (view == null)
                return;
            if (this.CanCloseView(view))
            {
                var workspace = this.Workspace;
                ApplicationCommands.Close.Execute(view, workspace as IInputElement);
            }
        }

        /// <summary>
        /// Called when an input element requests whether a given view can be closed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        protected virtual void OnCanExecuteClose(object sender, CanExecuteRoutedEventArgs e)
        {
            var view = e.Parameter as ApplicationView;
            if (view == null)
            {
                e.CanExecute = false;
                return;
            }
            e.CanExecute = this.CanCloseView(view);
        }

        private bool CanCloseView(ApplicationView view)
        {
            var workspace = this.Workspace;
            if (!(workspace is IInputElement))
                return false;

            if (!ApplicationCommands.Close.CanExecute(view, workspace as IInputElement))
                return false;

            return true;
        }

        /// <summary>
        /// Gets or sets the workspace this tab strip shall work with.
        /// </summary>
        /// <value>The workspace.</value>
        public IWorkspace Workspace
        {
            get { return (IWorkspace)this.GetValue(WorkspaceProperty); }
            set { this.SetValue(WorkspaceProperty, value); }
        }

        /// <summary>
        /// Determines if the specified item is (or is eligible to be) its own ItemContainer.
        /// </summary>
        /// <param name="item">Specified item.</param>
        /// <returns>
        /// Returns true if the item is its own ItemContainer; otherwise, false.
        /// </returns>
        protected override sealed bool IsItemItsOwnContainerOverride(object item)
        {
            return false;
        }

        /// <summary>
        /// Prepares the specified element to display the specified item.
        /// </summary>
        /// <param name="element">Element used to display the specified item.</param>
        /// <param name="item">Specified item.</param>
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            ((WorkspaceTabItem)element).HeaderTemplate = this.ItemTemplate;
            ((WorkspaceTabItem)element).Header = item;
            ((WorkspaceTabItem)element).SetBinding(TabItem.IsSelectedProperty, new Binding { Source = item, Path = new PropertyPath("IsActive"), Mode = BindingMode.OneWay });
            base.PrepareContainerForItemOverride(element, item);
        }

        /// <summary>
        /// Creates or identifies the element used to display the specified item.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Windows.Controls.TabItem"/>.
        /// </returns>
        protected override sealed DependencyObject GetContainerForItemOverride()
        {
            return this.CreateContainer();
        }

        /// <summary>
        /// Creates a container item.
        /// </summary>
        /// <returns></returns>
        protected virtual WorkspaceTabItem CreateContainer()
        {
            return new WorkspaceTabItem();
        }

        /// <summary>
        /// Called when the Workspace property has changed.
        /// </summary>
        /// <param name="oldValue">The old value.</param>
        /// <param name="newValue">The new value.</param>
        protected virtual void OnWorkspaceChanged(IMultiViewWorkspace oldValue, IMultiViewWorkspace newValue)
        {
            if (newValue != null)
                this.SetBinding(ItemsSourceProperty, new Binding() { Source = newValue, Path = new PropertyPath("Items"), Mode = BindingMode.OneWay });
            else
                this.ClearValue(ItemsSourceProperty);
            CommandManager.InvalidateRequerySuggested();
        }
    }
}